use libc::{
    __errno_location, c_char, c_long, chdir, mkdir, mount, printf, rmdir, strerror, umount2,
    MNT_DETACH, MS_BIND, MS_NODEV, MS_NOEXEC, MS_NOSUID, MS_REC,
};
use nix::errno::Errno;
use nix::unistd::pivot_root;
use std::ffi::{CStr, CString};
use std::ptr::null;

pub fn ns_rootfs_setup() -> Result<(), String> {
    unsafe {
        let new_root = c"/home/rootfs".as_ptr();
        let old_root = c"/home/rootfs/.old_root".as_ptr();
        let i = mount(new_root, new_root, null(), MS_BIND | MS_REC, null());
        if i == -1 {
            printf(c"%s\n".as_ptr(), strerror(*__errno_location()));
            return Err("err remount failed".into());
        };

        if mkdir(old_root, 0777) < 0 {
            printf(c"%s\n".as_ptr(), strerror(*__errno_location()));
            return Err("err mkdir failed".into());
        };
        let i1 = libc::syscall(libc::SYS_pivot_root, new_root, old_root);
        Errno::result(i1).map_err(|e| e.to_string())?;
        if chdir(c"/".as_ptr()) < 0 {
            printf(c"%s\n".as_ptr(), strerror(*__errno_location()));
            return Err("err chdir / failed".into());
        };
        match ns_fs_proc_mount() {
            Ok(_) => {}
            Err(e) => { println!("{}", e); }
        }
        if umount2(c"/.old_root".as_ptr(), MNT_DETACH) < 0 {
            printf(c"%s\n".as_ptr(), strerror(*__errno_location()));
            return Err("err umount old root fail".into());
        }
        if rmdir(c"/.old_root".as_ptr()) < 0 {
            printf(c"%s\n".as_ptr(), strerror(*__errno_location()));
            return Err("err rm dir fail".into());
        };

        Ok(())
    }
}
pub fn ns_fs_proc_mount() -> Result<(), String> {
    unsafe {
        if mount(
            c"proc".as_ptr(),
            c"/proc".as_ptr(),
            c"proc".as_ptr(),
            // MS_NOSUID | MS_NOEXEC | MS_NODEV,
            0,
            null(),
        ) == -1
        {
            printf(c"%s\n".as_ptr(), strerror(*__errno_location()));
            return Err("err mount proc failed".into());
        };
        Ok(())
    }
}
